    // Copyright (C) 2003  Davis E. King (davis@dlib.net), Miguel Grinberg
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_SOCKETS_KERNEl_2_
    #define DLIB_SOCKETS_KERNEl_2_

    #ifdef DLIB_ISO_CPP_ONLY
    #error "DLIB_ISO_CPP_ONLY is defined so you can't use this OS dependent code.  Turn DLIB_ISO_CPP_ONLY off if you want to use it."
    #endif

    #include "../platform.h"

    #include "sockets_kernel_abstract.h"

    #define _BSD_SOCKLEN_T_

    #include <ctime>
    #include <memory>
    #include <string>

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <errno.h>

    #ifndef HPUX
    #include <sys/select.h>
    #endif
    #include <arpa/inet.h>
    #include <signal.h>
    #include <inttypes.h>
    #include <netdb.h>
    #include <unistd.h>
    #include <sys/param.h>

    #include <netinet/in.h>

    #include "../threads.h"
    #include "../algs.h"

    namespace dlib
    {

    // ----------------------------------------------------------------------------------------

        // forward declarations
        class socket_factory;
        class listener;

    // ----------------------------------------------------------------------------------------

        // lookup functions

        int
        get_local_hostname (
            std::string& hostname
        );

    // -----------------

        int
        hostname_to_ip (
            const std::string& hostname,
            std::string& ip,
            int n = 0
        );

    // -----------------

        int
        ip_to_hostname (
            const std::string& ip,
            std::string& hostname
        );

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // connection object
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        class connection
        {
            /*!
                INITIAL_VALUE
                    sd                      == false
                    sdo                     == false
                    sdr                     == 0

                CONVENTION
                    connection_socket       == the socket handle for this connection.
                    connection_foreign_port == the port that foreign host is using for
                                               this connection
                    connection_foreign_ip   == a string containing the IP address of the
                                               foreign host
                    connection_local_port   == the port that the local host is using for
                                               this connection
                    connection_local_ip     == a string containing the IP address of the
                                               local interface being used by this connection

                    sd                      == if shutdown() has been called then true
                                               else false
                    sdo                     == if shutdown_outgoing() has been called then true
                                               else false
                    sdr                     == the return value of shutdown() if it has been
                                               called.  if it hasn't been called then 0

            !*/

            friend class listener;                // make listener a friend of connection
            // make create_connection a friend of connection
            friend int create_connection (
                connection*& new_connection,
                unsigned short foreign_port,
                const std::string& foreign_ip,
                unsigned short local_port,
                const std::string& local_ip
            );

        public:

            ~connection();

            void* user_data;

            long write (
                const char* buf,
                long num
            );

            long read (
                char* buf,
                long num
            );

            long read (
                char* buf,
                long num,
                unsigned long timeout
            );

            int get_local_port (
            ) const { return connection_local_port; }

            int get_foreign_port (
            ) const { return connection_foreign_port; }

            const std::string& get_local_ip (
            ) const { return connection_local_ip; }

            const std::string& get_foreign_ip (
            ) const { return connection_foreign_ip; }

            int shutdown_outgoing (
            )
            {
                sd_mutex.lock();
                if (sdo || sd)
                {
                    sd_mutex.unlock();
                    return sdr;
                }
                sdo = true;
                sdr = ::shutdown(connection_socket,SHUT_WR);
                int temp = sdr;
                sd_mutex.unlock();
                return temp;
            }

            int shutdown (
            )
            {
                sd_mutex.lock();
                if (sd)
                {
                    sd_mutex.unlock();
                    return sdr;
                }
                sd = true;
                sdr = ::shutdown(connection_socket,SHUT_RDWR);
                int temp = sdr;
                sd_mutex.unlock();
                return temp;
            }

            int disable_nagle(
            );

            typedef int socket_descriptor_type;

            socket_descriptor_type get_socket_descriptor (
            ) const { return connection_socket; }

        private:

            bool readable (
                unsigned long timeout
            ) const;
            /*!
                requires
                    - timeout < 2000000
                ensures
                    - returns true if a read call on this connection will not block.
                    - returns false if a read call on this connection will block or if
                      there was an error.
            !*/

            bool sd_called (
            )const
            /*!
                ensures
                    - returns true if shutdown() has been called else
                    - returns false
            !*/
            {
                sd_mutex.lock();
                bool temp = sd;
                sd_mutex.unlock();
                return temp;
            }

            bool sdo_called (
            )const
            /*!
                ensures
                    - returns true if shutdown_outgoing() or shutdown() has been called
                      else returns false
            !*/
            {
                sd_mutex.lock();
                bool temp = false;
                if (sdo || sd)
                    temp = true;
                sd_mutex.unlock();
                return temp;
            }

            // data members
            int connection_socket;
            const int connection_foreign_port;
            const std::string connection_foreign_ip;
            const int connection_local_port;
            const std::string connection_local_ip;

            bool sd;  // called shutdown
            bool sdo; // called shutdown_outgoing
            int sdr; // return value for shutdown
            mutex sd_mutex; // a lock for the three above vars

            connection(
                int sock,
                int foreign_port,
                const std::string& foreign_ip,
                int local_port,
                const std::string& local_ip
            );
            /*!
                requires
                    - sock is a socket handle
                    - sock is the handle for the connection between foreign_ip:foreign_port
                      and local_ip:local_port
                ensures
                    - *this is initialized correctly with the above parameters
            !*/

            // restricted functions
            connection();
            connection(connection&);        // copy constructor
            connection& operator=(connection&);    // assignement opertor
        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // listener object
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        class listener
        {
            /*!
                CONVENTION
                    if (inaddr_any == false)
                    {
                        listening_ip == a string containing the address the listener is
                                        listening on
                    }
                    else
                    {
                        the listener is listening on all interfaces
                    }

                    listening_port == the port the listener is listening on
                    listening_socket == the listening socket handle for this object
            !*/

            // make the create_listener a friend of listener
            friend int create_listener (
                listener*& new_listener,
                unsigned short port,
                const std::string& ip
            );

        public:

            ~listener();

            int accept (
                connection*& new_connection,
                unsigned long timeout = 0
            );

            int accept (
                std::unique_ptr<connection>& new_connection,
                unsigned long timeout = 0
            );

            int get_listening_port (
            ) const { return listening_port; }

            const std::string& get_listening_ip (
            ) const { return listening_ip; }

        private:

            // data members
            int listening_socket;
            const int listening_port;
            const std::string listening_ip;
            const bool inaddr_any;

            listener(
                int sock,
                int port,
                const std::string& ip
            );
            /*!
                requires
                    - sock is a socket handle
                    - sock is listening on the port and ip(may be "") indicated in the above
                      parameters
                ensures
                    - *this is initialized correctly with the above parameters
            !*/

            // restricted functions
            listener();
            listener(listener&);        // copy constructor
            listener& operator=(listener&);    // assignement opertor
        };

    // ----------------------------------------------------------------------------------------

        int create_listener (
            listener*& new_listener,
            unsigned short port,
            const std::string& ip = ""
        );

        int create_connection (
            connection*& new_connection,
            unsigned short foreign_port,
            const std::string& foreign_ip,
            unsigned short local_port = 0,
            const std::string& local_ip = ""
        );

        int create_listener (
            std::unique_ptr<listener>& new_listener,
            unsigned short port,
            const std::string& ip = ""
        );

        int create_connection (
            std::unique_ptr<connection>& new_connection,
            unsigned short foreign_port,
            const std::string& foreign_ip,
            unsigned short local_port = 0,
            const std::string& local_ip = ""
        );

    // ----------------------------------------------------------------------------------------

    }

    #ifdef NO_MAKEFILE
    #include "sockets_kernel_2.cpp"
    #endif

    #endif // DLIB_SOCKETS_KERNEl_2_